今天來聊聊 useRef
這個比較容易理解的 hook function,這期的內容,多半是我自己使用經驗的總結,也許不是那麼精確,但也多多少少能提供新手理解,那麼我們先從他的演進開始吧!
在 class component 中 ref
是必須透過 createRef()
才能長出來給元件使用的東西。以我的理解來說,就是我長出一個dom的節點資料細節的監聽事件,但我不做細部updating的解析,取用時再去針對細部內容做比對。
import React, { Component } from 'react';
class InputField extends Component {
constructor(props) {
super(props);
// 要先創ref
this.inputRef = React.createRef();
}
focusInput = () => {
this.inputRef.current.focus();
};
render() {
return (
<div>
<input ref={this.inputRef} />
<button onClick={this.focusInput}>Focus Input</button>
</div>
);
}
}
export default InputField;
那麼在轉換成 functional component 之後就簡化成現在的 useRef
了,如範例:
import React, { useRef } from 'react';
// function component 的簡化
function InputField() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
export default InputField;
那麼,他與 useState
看似相同的功用到底有什麼好處呢?我下面舉個例子來讓大家複習 useRef
的好處:
import { useRef, useState } from "react";
// 在開始之前我先示範一個useState綁定input 範例
const RefExample = () => {
// useState 的做法
const [state, setState] = useState("");
const onStateChange = (e) => {
setState(e.target.value);
};
// useRef 的差異
// 你可以想像它為useState的進階用法,
// 用它來處理表單的好處就是能夠有效控制重複拿取state的問題,
// 可以優化react的運行。
const inputRef = useRef(null);
// 當你打開以下兩個 console 的時候你就能理解為何會說它能優化的部分了
// console.log("with state", state);
// console.log("ref", inputRef);
// 如果是用 state 綁定的話,所有的 setState 會促使 component 觸發 reredning
// 但一樣的東西改用 useRef 來做的話,它就只是單純綁定在你設定的 html tag 之下,
// 等到你需要觸發使用該值的時候,才會去更動 current 內的資料進行 rerending
return (
<div>
<div>
<h4>useRef 使用差異</h4>
</div>
<fieldset>
<legend>用 useState 綁定</legend>
<input type="text" value={state} onChange={onStateChange} />
<button
onClick={() => console.log(state)}
style={{
margin: "0 1rem"
}}
>
check
</button>
</fieldset>
<fieldset>
<legend>用 useRef 綁定</legend>
<input type="text" ref={inputRef} />
<button
onClick={() => console.log(inputRef.current?.value)}
style={{
margin: "0 1rem"
}}
>
check
</button>
</fieldset>
</div>
);
};
export default RefExample;
這裡只要把我註解掉的 console 打開就可以知道其中的差異了,你會發現範例中使用 useState
綁定的 input 會因為 onChange
的 setState
導致觸發 state updating 造成重新渲染:
而相反的 useRef 的使用情況下就相對簡單,只需要設定完預設值之後,透過 ref
註冊在相對應下的 dom 節點,那麼在他底下的所有事件都能透過 current 去拿到,範例中的 value 就是透過已經套用 ref
註冊的 input 去提取它的 value,這樣的方式就不會像 useState
一樣觸發 updating 而造成重新渲染:
這對減少重複性渲染確實很有幫助,尤其是在表單這類的使用情境中,我不需要在用戶尚未送出資料的時候一直重新渲染,算是一種比較容易理解的效能優化。
那能不能將所有的
useState
用useRef
來取代呢?
答案也是否定的,要看你的使用情境是不是要一直追蹤使用者的操作,如果需要的話我相信 useState
會是比較好的選擇,如果不用的話那也許應該回頭看看 useRef
是否比較能勝任這類的需求。
以上就是今天的分享,下一篇會提及效能優化常提到的memo。